- 📌 김영한의 실전 데이터베이스 - 기본편
- 📌 기간 : 2025.10.02 ~ 2025.10.09
1. 서론
데이터베이스를 사용하면서도 SQL 문법이나 인덱스, 트랜잭션 등의 개념을 단편적으로 알고 있는 경우가 많아 기본 개념을 체계적으로 다시 정리하기 위해 선택한 강의였습니다.
특히 인상 깊었던 부분은 단순히 SQL 문법을 익히는 것이 아니라,
- 왜 테이블을 분리하고 JOIN을 사용하는지
- 왜 인덱스를 사용하는지
- 옵티마이저는 어떤 기준으로 실행 계획을 선택하는지
- 트랜잭션이 왜 필요한지
등 각 기능이 왜 필요한지를 이해하는 것에 초점을 맞춘 점이었습니다.
2. 내용 정리
JOIN
-
테이블을 분리하는 이유
- 데이터 중복을 줄이고 데이터의 일관성을 유지하기 위해 사용한다.
- 하나의 테이블에 모든 데이터를 저장하기보다 관심사에 따라 테이블을 분리하고 필요한 경우 JOIN을 통해 데이터를 연결한다.
-
JOIN
- 2개 이상의 테이블을 특정 조건을 기준으로 연결하여 데이터를 조회하는 기능이다.
-
INNER JOIN
- 두 테이블에서 JOIN 조건이 일치하는 데이터만 조회한다.
- 가장 기본적인 JOIN 방식이다.
-
LEFT JOIN
- 왼쪽에 위치한 기준 테이블의 모든 데이터를 조회한다.
- 오른쪽 테이블과 일치하는 데이터가 없는 경우 NULL로 조회된다.
-
RIGHT JOIN
- 오른쪽 테이블을 기준으로 모든 데이터를 조회한다.
- LEFT JOIN으로 동일한 결과를 만들 수 있기 때문에 실무에서는 상대적으로 덜 사용된다.
-
SELF JOIN
- 하나의 테이블을 자기 자신과 JOIN하는 방식이다.
- 조직도나 카테고리처럼 계층형 데이터를 표현할 때 활용할 수 있다.
-
CROSS JOIN
- 두 테이블의 모든 행을 조합한다.
- 결과 건수는 일반적으로
N × M이 된다.
ㅍ 의도하지 않게 사용하면 매우 큰 결과가 발생할 수 있으므로 주의해야 한다.
-
복잡한 JOIN은 한 번에 해결하려 하기보다 요구사항을 작은 단위로 나누어 단계적으로 접근하는 것이 중요하다.
서브쿼리
- 서브쿼리(Subquery)
- 하나의 SQL 문 내부에 포함된 또 다른 SELECT 문이다.
- 특정 조건의 데이터를 먼저 조회하거나 계산한 결과를 외부 쿼리에서 활용할 수 있다.
- 최근의 데이터베이스 옵티마이저는 일부 서브쿼리를 내부적으로 JOIN과 유사한 형태로 최적화하기도 한다.
- 따라서 단순히 **“서브쿼리는 무조건 느리다”**라고 생각하기보다 실행 계획을 확인하고 판단하는 것이 중요하다.
- 가능한 경우 JOIN으로 표현하면 여러 테이블 간의 관계를 명확하게 표현할 수 있어 가독성과 유지보수 측면에서 유리할 수 있다.
IN과EXISTS는 상황에 따라 성능 차이가 발생할 수 있으므로 데이터 분포와 실행 계획을 기준으로 판단해야 한다.
UNION
- UNION
- 여러 SELECT 결과를 하나의 결과로 합친다.
- 결과에서 중복된 데이터를 제거한다.
- UNION ALL
- 여러 SELECT 결과를 그대로 합친다.
- 중복 제거를 수행하지 않는다.
- 중복 제거가 필요하지 않다면 일반적으로 UNION ALL을 사용하는 것이 성능상 유리하다.
- UNION은 중복 제거를 위한 추가적인 연산이 필요하기 때문이다.
CASE
- CASE 문
- 조건에 따라 서로 다른 값을 반환할 수 있는 SQL의 조건문이다.
- SELECT뿐만 아니라 ORDER BY, GROUP BY 등의 절에서도 활용할 수 있다.
- 데이터를 조회하면서 특정 조건에 따라 값을 변환하거나 분류할 때 유용하다.
- 예를 들어 상태 코드에 따라 사용자에게 보여줄 이름을 변경하거나 특정 조건에 따라 정렬 순서를 다르게 지정할 수 있다.
VIEW
- VIEW
- 하나 이상의 SELECT 문을 기반으로 만들어지는 논리적인 가상 테이블이다.
- 실제 데이터를 별도로 저장하기보다는 기존 테이블의 조회 결과를 하나의 테이블처럼 사용할 수 있도록 한다.
- 복잡한 조회 로직을 VIEW로 만들어 재사용할 수 있어 조회 로직의 추상화와 편의성에 도움이 된다.
- VIEW의 변경 가능 여부는 데이터베이스와 VIEW의 구성 방식에 따라 달라진다.
- 단순한 단일 테이블 기반 VIEW는 일부 DML이 가능할 수 있다.
- 여러 테이블을 JOIN하거나 집계 등을 포함하는 복잡한 VIEW는 변경이 제한될 수 있다.
- 따라서 VIEW는 기본적으로 조회 목적의 기능으로 이해하는 것이 좋다.
INDEX
- Full Table Scan
- 테이블의 데이터를 처음부터 끝까지 읽어 조건에 맞는 데이터를 찾는 방식이다.
- 데이터가 많아질수록 불필요하게 많은 데이터를 읽을 가능성이 있다.
- INDEX
- 원하는 데이터를 빠르게 찾기 위한 자료구조이다.
- 일반적으로 B-Tree 계열의 구조를 사용하며, 검색 조건에 적절한 인덱스가 있다면 전체 데이터를 모두 읽지 않고 필요한 데이터를 찾을 수 있다.
- 하지만 인덱스가 존재한다고 항상 인덱스를 사용하는 것은 아니다.
- 데이터베이스의 옵티마이저가 비용을 계산한 후 인덱스를 사용하는 것이 더 비효율적이라고 판단하면 Full Table Scan을 선택할 수도 있다.
MySQL의 인덱스 구조
- 클러스터형 인덱스(Clustered Index)
- InnoDB에서는 기본키를 중심으로 데이터가 저장되는 구조를 가진다.
- 기본키를 기준으로 데이터를 찾는 경우 효율적으로 접근할 수 있다.
- 보조 인덱스(Secondary Index)
- 기본키 이외의 컬럼에 생성하는 인덱스이다.
- InnoDB의 보조 인덱스에는 인덱스 키와 함께 해당 레코드의 기본키 값이 저장된다.
- 따라서 보조 인덱스를 이용해 데이터를 조회할 때는 상황에 따라 보조 인덱스 탐색, 기본키를 통한 데이터 접근과 같은 추가적인 작업이 발생할 수 있다.
- 이를 이해하면 **“인덱스를 만들었는데 왜 생각보다 빠르지 않은가?”**와 같은 문제를 분석하는 데 도움이 된다.
인덱스 설계
-
커버링 인덱스(Covering Index)
- 쿼리에서 필요한 컬럼이 인덱스에 모두 포함되어 있어 테이블의 실제 데이터를 추가로 조회하지 않고 인덱스만으로 결과를 처리할 수 있는 경우를 의미한다.
- 불필요한 테이블 접근을 줄일 수 있어 성능 향상에 도움이 될 수 있다.
-
복합 인덱스
- 여러 컬럼을 하나의 인덱스로 구성하는 방식이다.
- 컬럼의 순서가 매우 중요하다.
-
일반적으로 다음과 같은 원칙을 고려할 수 있다.
- 선행 컬럼부터 조건에 활용되는 것이 중요하다.
- 일반적으로 등호 조건을 먼저, 범위 조건을 뒤쪽에 배치하는 것을 고려한다.
- WHERE뿐만 아니라 ORDER BY, GROUP BY 등의 사용 패턴도 함께 고려한다.
-
결국 인덱스는 단순히 **“검색하는 컬럼에 무조건 만든다”**가 아니라 실제 SQL의 사용 패턴과 데이터 분포를 함께 고려해야 한다.
데이터 무결성
-
데이터 무결성
- 데이터가 정확하고 일관된 상태를 유지하도록 보장하는 것이다.
- 데이터 검증은 애플리케이션에서 처리하는 것이 유연할 수 있지만, 중요한 데이터 규칙까지 애플리케이션에만 의존하면 데이터베이스에 잘못된 데이터가 저장될 가능성이 있다.
- 따라서 애플리케이션 검증과 함께 DB의 제약조건을 적절하게 활용하는 것이 중요하다.
-
CHECK 제약조건
- 특정 컬럼에 저장될 수 있는 값의 조건을 지정한다.
- 예를 들어 가격이 음수가 되지 않도록 제한할 수 있다.
CREATE TABLE products ( ... price INT NOT NULL CHECK (price >= 0) );- 즉, 데이터 무결성을 애플리케이션과 데이터베이스가 함께 보장하는 구조로 생각할 수 있다.
트랜잭션
-
트랜잭션(Transaction)
- 여러 데이터베이스 작업을 하나의 논리적인 작업 단위로 묶는 개념이다.
-
All or Nothing
- 트랜잭션에 포함된 작업은 모두 성공하거나 모두 실패해야 한다.
- 중간 단계까지만 반영되어 데이터가 불완전한 상태로 남는 것을 방지한다.
-
예를 들어 주문을 생성하면서 결제 정보를 저장하고 재고를 차감하는 작업이 있다면, 일부 작업만 성공해서는 안 된다.
-
트랜잭션을 통해 데이터의 일관성과 안정성을 유지할 수 있다.
-
MySQL은 기본적으로 **자동 커밋(Auto Commit)**이 활성화되어 있다.
- 하나의 SQL이 실행된 후 자동으로 커밋되는 방식이다.
- 여러 SQL을 하나의 작업 단위로 처리해야 하는 경우 명시적으로 트랜잭션을 관리해야 한다.
저장 프로시저와 함수
-
저장 프로시저(Stored Procedure)
- 데이터베이스 서버에 SQL 로직을 미리 정의해두고 필요할 때 호출하는 방식이다.
- 여러 SQL을 하나의 작업 단위로 구성할 수 있다.
-
함수(Function)
-
입력값을 받아 특정 계산이나 처리를 수행하고 결과값을 반환하는 형태의 데이터베이스 객체이다.
-
SQL에서 값을 계산하거나 변환하는 용도로 사용할 수 있다.
-
프로시저와 함수의 차이
- 프로시저는 여러 작업을 수행할 수 있으며 반드시 값을 반환할 필요는 없다.
- 함수는 기본적으로 입력값을 받아 결과값을 반환하는 것을 목적으로 한다.
3. 정리
- JOIN은 분리된 데이터를 연결하기 위한 핵심 기능이다.
- 서브쿼리는 상황에 따라 JOIN과 함께 적절한 방식을 선택해야 한다.
- 중복 제거가 필요하지 않다면 UNION보다 UNION ALL을 사용하는 것이 효율적이다.
- VIEW는 복잡한 조회 로직을 추상화하고 재사용하는 데 활용할 수 있다.
- 인덱스는 검색 성능을 향상시키지만 무조건 사용하는 것이 아니며 옵티마이저의 실행 계획을 함께 이해해야 한다.
- 복합 인덱스는 컬럼의 순서와 실제 SQL의 사용 패턴이 중요하다.
- 데이터 무결성은 애플리케이션뿐만 아니라 데이터베이스의 제약조건을 통해서도 보호할 수 있다.
- 트랜잭션은 여러 작업을 하나의 논리적인 단위로 묶어 데이터의 일관성을 보장한다.